#include "glshadermanager.h"
#include <iostream>
#include <QDebug>
#include <glutils.h>

using namespace std;

glShaderManager::glShaderManager()
{
    _nInputPrimitiveType = GL_TRIANGLES;
    _nOutputPrimitiveType = GL_TRIANGLE_STRIP;
    _nVerticesOut = 3;
}

glShaderManager::~glShaderManager()
{
    std::map<std::string, glShader*>::iterator pbegin = m_shaderPrograms.begin();
    std::map<std::string, glShader*>::iterator pend = m_shaderPrograms.end();

    for(; pbegin != pend; ++pbegin)
    {
        delete pbegin->second;
    }
}

glShaderManager* glShaderManager::pInstance = NULL;

glShaderManager* glShaderManager::getInstance()
{
    if (pInstance == NULL)
        pInstance = new glShaderManager();
    return pInstance;
}

void glShaderManager::deleteInstance()
{
    delete pInstance;
}

// ----------------------------------------------------------------------------

void glShaderManager::SetInputPrimitiveType(int nInputPrimitiveType)
{
    _nInputPrimitiveType = nInputPrimitiveType;

}

void glShaderManager::SetOutputPrimitiveType(int nOutputPrimitiveType)
{
    _nOutputPrimitiveType = nOutputPrimitiveType;

}

void glShaderManager::SetVerticesOut(int nVerticesOut)
{
    _nVerticesOut = nVerticesOut;
}

// ----------------------------------------------------------------------------
glShader* glShaderManager::loadfromFile(char* vertexFile, char* fragmentFile)
{
    glShader* o = new glShader();
    o->UsesGeometryShader(false);

    aVertexShader* tVertexShader = new aVertexShader;
    aFragmentShader* tFragmentShader = new aFragmentShader;

    // load vertex program
    if (vertexFile!=0)
        if (tVertexShader->load(vertexFile) != 0)
        {
            qDebug() << "error: can't load vertex shader!\n";
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            return 0;
        }


    // Load fragment program
    if (fragmentFile!=0)
        if (tFragmentShader->load(fragmentFile) != 0)
        {
            qDebug() << "error: can't load fragment shader!\n";
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            return 0;
        }

    // Compile vertex program
    if (vertexFile!=0)
        if (!tVertexShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Vertex Shader):\n";
            qDebug() << tVertexShader->getCompilerLog() << endl;
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            return 0;
        }
    qDebug() << "***GLSL Compiler Log (Vertex Shader):\n";
    qDebug() << tVertexShader->getCompilerLog() << "\n";

    // Compile fragment program
    if (fragmentFile!=0)
        if (!tFragmentShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Fragment Shader):\n";
            qDebug() << tFragmentShader->getCompilerLog() << endl;

            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            return 0;

        }
    qDebug() << "***GLSL Compiler Log (Fragment Shader):\n";
    qDebug() << tFragmentShader->getCompilerLog() << "\n";

    // Add to object
    if (vertexFile!=0) o->addShader(tVertexShader);
    if (fragmentFile!=0) o->addShader(tFragmentShader);

    // link
    if (!o->link())
    {
        qDebug() << "**LINKER ERROR\n";
        qDebug() << o->getLinkerLog() << endl;
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        return 0;
    }
    qDebug() << "***GLSL Linker Log:\n";
    qDebug() << o->getLinkerLog() << endl;

    _shaderObjectList.push_back(o);
    o->manageMemory();

    return o;
}


glShader* glShaderManager::loadfromFile(std::string name, char* vertexFile, char* geometryFile, char* fragmentFile)
{
    //shader program
    glShader* o = new glShader();

    if (geometryFile != 0)
    {
        o->UsesGeometryShader(true);
        o->SetInputPrimitiveType(_nInputPrimitiveType);
        o->SetOutputPrimitiveType(_nOutputPrimitiveType);
        o->SetVerticesOut(_nVerticesOut);
    }
    else
        o->UsesGeometryShader(false);

    aVertexShader* tVertexShader = new aVertexShader;
    aFragmentShader* tFragmentShader = new aFragmentShader;
    aGeometryShader* tGeometryShader = new aGeometryShader;

    // load vertex program
    if (vertexFile!=0)
        if (tVertexShader->load(vertexFile) != 0)
        {
            qDebug() << "error: can't load vertex shader!\n";

            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;
        }

    // Load geometry program
    if (geometryFile!=0)
        if (tGeometryShader->load(geometryFile) != 0)
        {
            qDebug() << "error: can't load geometry shader!\n";
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;
        }

    // Load fragment program
    if (fragmentFile!=0)
        if (tFragmentShader->load(fragmentFile) != 0)
        {
            qDebug() << "error: can't load fragment shader!\n";
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;
        }

    // Compile vertex program
    if (vertexFile!=0)
        if (!tVertexShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Vertex Shader):\n";
            qDebug() << tVertexShader->getCompilerLog() << endl;
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;
        }
    qDebug() << "***GLSL Compiler Log (Vertex Shader):\n";
    qDebug() << tVertexShader->getCompilerLog() << "\n";

    // Compile geometry program
    if (geometryFile!=0)
    {

        if (!tGeometryShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Geometry Shader):\n";
            qDebug() << tGeometryShader->getCompilerLog() << endl;

            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;

        }
    }
    qDebug() << "***GLSL Compiler Log (Geometry Shader):\n";
    qDebug() << tGeometryShader->getCompilerLog() << "\n";

    // Compile fragment program
    if (fragmentFile!=0)
        if (!tFragmentShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Fragment Shader):\n";
            qDebug() << tFragmentShader->getCompilerLog() << endl;

            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;

        }
    qDebug() << "***GLSL Compiler Log (Fragment Shader):\n";
    qDebug() << tFragmentShader->getCompilerLog() << "\n";

    // Add to object
    if (vertexFile!=0) o->addShader(tVertexShader);
    if (geometryFile!=0) o->addShader(tGeometryShader);
    if (fragmentFile!=0) o->addShader(tFragmentShader);

    // link
    if (!o->link())
    {
        qDebug() << "**LINKER ERROR\n";
        qDebug() << o->getLinkerLog() << endl;
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
    }
    qDebug() << "***GLSL Linker Log:\n";
    qDebug() << o->getLinkerLog() << endl;

   //add program to the list;
    m_shaderPrograms[name] = o;
    o->manageMemory();

    return o;
    return 0;
}
// ----------------------------------------------------------------------------

glShader* glShaderManager::loadfromMemory(const char* vertexMem, const char* fragmentMem)
{
    glShader* o = new glShader();
    o->UsesGeometryShader(false);

    aVertexShader* tVertexShader = new aVertexShader;
    aFragmentShader* tFragmentShader = new aFragmentShader;

    // get vertex program
    if (vertexMem!=0)
        tVertexShader->loadFromMemory(vertexMem);

    // get fragment program
    if (fragmentMem!=0)
        tFragmentShader->loadFromMemory(fragmentMem);

    // Compile vertex program
    if (vertexMem!=0)
        if (!tVertexShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Vertex Shader):\n";
            qDebug() << tVertexShader->getCompilerLog() << endl;
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            return 0;
        }
    qDebug() << "***GLSL Compiler Log (Vertex Shader):\n";
    qDebug() << tVertexShader->getCompilerLog() << "\n";

    // Compile fragment program
    if (fragmentMem!=0)
        if (!tFragmentShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Fragment Shader):\n";
            qDebug() << tFragmentShader->getCompilerLog() << endl;

            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            return 0;
        }
    qDebug() << "***GLSL Compiler Log (Fragment Shader):\n";
    qDebug() << tFragmentShader->getCompilerLog() << "\n";

    // Add to object
    if (vertexMem!=0) o->addShader(tVertexShader);
    if (fragmentMem!=0) o->addShader(tFragmentShader);

    // link
    if (!o->link())
    {
        qDebug() << "**LINKER ERROR\n";
        qDebug() << o->getLinkerLog() << endl;
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        return 0;
    }
    qDebug() << "***GLSL Linker Log:\n";
    qDebug() << o->getLinkerLog() << endl;

    _shaderObjectList.push_back(o);
    o->manageMemory();

    return o;
}

glShader* glShaderManager::loadfromMemory(const char* vertexMem, const char* geometryMem, const char* fragmentMem)
{
    glShader* o = new glShader();
    o->UsesGeometryShader(true);
    o->SetInputPrimitiveType(_nInputPrimitiveType);
    o->SetOutputPrimitiveType(_nOutputPrimitiveType);
    o->SetVerticesOut(_nVerticesOut);

    aVertexShader* tVertexShader = new aVertexShader;
    aFragmentShader* tFragmentShader = new aFragmentShader;
    aGeometryShader* tGeometryShader = new aGeometryShader;

    // get vertex program
    if (vertexMem!=0)
        tVertexShader->loadFromMemory(vertexMem);

    // get fragment program
    if (fragmentMem!=0)
        tFragmentShader->loadFromMemory(fragmentMem);

    // get fragment program
    if (geometryMem!=0)
        tGeometryShader->loadFromMemory(geometryMem);

    // Compile vertex program
    if (vertexMem!=0)
        if (!tVertexShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Vertex Shader):\n";
            qDebug() << tVertexShader->getCompilerLog() << endl;
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;
        }
    qDebug() << "***GLSL Compiler Log (Vertex Shader):\n";
    qDebug() << tVertexShader->getCompilerLog() << "\n";

    // Compile geometry program
    if (geometryMem!=0)
        if (!tGeometryShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Geometry Shader):\n";
            qDebug() << tGeometryShader->getCompilerLog() << endl;
            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;
        }
    qDebug() << "***GLSL Compiler Log (Geometry Shader):\n";
    qDebug() << tVertexShader->getCompilerLog() << "\n";

    // Compile fragment program
    if (fragmentMem!=0)
        if (!tFragmentShader->compile())
        {
            qDebug() << "***COMPILER ERROR (Fragment Shader):\n";
            qDebug() << tFragmentShader->getCompilerLog() << endl;

            delete o;
            delete tVertexShader;
            delete tFragmentShader;
            delete tGeometryShader;
            return 0;
        }
    qDebug() << "***GLSL Compiler Log (Fragment Shader):\n";
    qDebug() << tFragmentShader->getCompilerLog() << "\n";

    // Add to object
    if (vertexMem!=0) o->addShader(tVertexShader);
    if (geometryMem!=0) o->addShader(tGeometryShader);
    if (fragmentMem!=0) o->addShader(tFragmentShader);

    // link
    if (!o->link())
    {
        qDebug() << "**LINKER ERROR\n";
        qDebug() << o->getLinkerLog() << endl;
        delete o;
        delete tVertexShader;
        delete tFragmentShader;
        delete tGeometryShader;
        return 0;
    }
    qDebug() << "***GLSL Linker Log:\n";
    qDebug() << o->getLinkerLog() << endl;

    _shaderObjectList.push_back(o);
    o->manageMemory();

    return o;
}

// ----------------------------------------------------------------------------

bool  glShaderManager::free(glShader* o)
{
    std::map<std::string, glShader*>::iterator pbegin = m_shaderPrograms.begin();
    std::map<std::string, glShader*>::iterator pend = m_shaderPrograms.end();

    for(; pbegin != pend; ++pbegin)
    {
        if (o == pbegin->second)
        {
            m_shaderPrograms.erase(pbegin);
            delete o;
            return true;
        }
    }
    return false;
}

glShader* glShaderManager::operator[](const std::string &programname)
{
    std::map<std::string, glShader*>::const_iterator program = m_shaderPrograms.find(programname);
    if (program != m_shaderPrograms.end())
        return program->second;
    else
    {
        qDebug() <<"Warning Program not known in [] "<<programname.c_str();
        return 0;
    }
}
